package com.ponhu.ea2.stockcenter.provider.service.biz.stocksales;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ponhu.ea2.stockcenter.enums.DefinedSystem;
import com.ponhu.ea2.stockcenter.provider.constants.CommonConstant;
import com.ponhu.ea2.stockcenter.provider.entity.StockInventoryChangeSettings;
import com.ponhu.ea2.stockcenter.provider.entity.StockInventoryReal;
import com.ponhu.ea2.stockcenter.provider.entity.StockInventoryUnreal;
import com.ponhu.ea2.stockcenter.provider.service.biz.CalcStockDataService;
import com.ponhu.ea2.stockcenter.provider.service.entity.*;
import com.ponhu.ea2.stockcenter.provider.support.enums.StockChangeInnerEventEnum;
import com.ponhu.ea2.stockcenter.provider.support.exception.StockRuntimeException;
import com.ponhu.ea2.stockcenter.provider.support.vo.InventoryInfoVO;
import com.ponhu.ea2.stockcenter.provider.support.vo.SalesLevelInventoryChangeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.ponhu.ea2.stockcenter.enums.ResponseCodeEnum.*;

/**
 * 销售层库存数据变动核心处理逻辑
 */
@Slf4j
@Service
public class SalesStockChangeCoreHandler {

    @Autowired
    private StockInventoryChangeSettingsService inventoryChangeSettingsService;

    @Autowired
    private StockInventoryRealService stockInventoryRealService;

    @Autowired
    private StockInventoryUnrealService stockInventoryUnrealService;

    @Autowired
    private StockInventoryChangeService stockInventoryChangeService;
    @Autowired
    private StockWarehouseRelationService stockWarehouseRelationService;

    public boolean handleStockChangeInnerEvent(String docNo, DefinedSystem definedSystem,
                                               StockChangeInnerEventEnum changeInnerEventEnum,
                                               List<InventoryInfoVO> inventoryInfoList) {
        //获取配置数据
        StockInventoryChangeSettings changeSettings = inventoryChangeSettingsService.getInventoryChangeSettingsByType(
                definedSystem.ordinal(), changeInnerEventEnum.getCode());
        //校验配置数据是否正确
        checkChangeSettingsIsCorrect(definedSystem, changeInnerEventEnum, changeSettings);


        //1. ============>>实物类货品<<=================
        List<StockInventoryReal> inventoryRealList = stockInventoryRealService.getRealInventoryStockList(
                inventoryInfoList.stream().map(InventoryInfoVO::getInventoryCode).collect(Collectors.toList())
        );

        //校验实物类货品库存数据是否正确
        checkRealInventoryDataIsCorrect(definedSystem, changeInnerEventEnum, inventoryInfoList, inventoryRealList);

        //获取虚拟类货品库存数据
        List<StockInventoryUnreal> inventoryUnrealList = stockInventoryUnrealService.getUnRealInventoryStockList(
                inventoryInfoList.stream().map(InventoryInfoVO::getInventoryCode).collect(Collectors.toList())
        );

        //更新销售层库存数据
        for (StockInventoryReal i : inventoryRealList) {
            InventoryInfoVO info = inventoryInfoList.stream().filter(f -> f.getInventoryCode().equals(i.getInventoryCode())).findFirst().get();

            //计算并设置新的实物类库存值
            calcAndSetNewRealStockNum(definedSystem, changeInnerEventEnum, changeSettings, inventoryUnrealList, i, info);
        }

        //更新实物类库存数
        stockInventoryRealService.updateBatchById(inventoryRealList);


        //2.  ============>>虚拟类货品<<=================
        //判断如果是虚拟类货品，可售数量发生变化，那么某些事件需要做"相反处理"

        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.NO) {
            saveSalesLevelStockChangeLog(docNo, definedSystem.ordinal(), changeInnerEventEnum.getCode(), inventoryInfoList, inventoryRealList, new ArrayList<>());
            return true;
        }

        //更新虚拟类货品库存（只有可售数量发生变化时需要处理虚拟货品库存）
        List<StockInventoryUnreal> changedUnrealList = new ArrayList<>();
        inventoryUnrealList = stockInventoryUnrealService.getUnRealInventoryStockList(
                inventoryInfoList.stream().map(InventoryInfoVO::getInventoryCode).collect(Collectors.toList())
        );

        if (CollectionUtils.isNotEmpty(inventoryUnrealList)) {
            for (StockInventoryUnreal i : inventoryUnrealList) {
                //计算并设置新的虚拟类库存值
                calcAndSetNewUnrealStockNum(inventoryInfoList, changeSettings, i);
                changedUnrealList.add(i);
            }
            stockInventoryUnrealService.updateBatchById(inventoryUnrealList);
        }

        //3.记录日志
        //如果配置表变动类型是3或4，那么记录虚拟类库存日志,否则不记录
        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_PLUS_AND_UNREAL_SUBTRACT
                || changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_SUBTRACT_AND_UNREAL_PLUS) {
            saveSalesLevelStockChangeLog(docNo, definedSystem.ordinal(), changeInnerEventEnum.getCode(), inventoryInfoList, inventoryRealList, changedUnrealList);
        }else {
            saveSalesLevelStockChangeLog(docNo, definedSystem.ordinal(), changeInnerEventEnum.getCode(), inventoryInfoList, inventoryRealList, new ArrayList<>());
        }

        return true;
    }

    private void calcAndSetNewUnrealStockNum(List<InventoryInfoVO> inventoryInfoList, StockInventoryChangeSettings changeSettings, StockInventoryUnreal i) {
        InventoryInfoVO info = inventoryInfoList.stream().filter(f -> f.getInventoryCode().equals(i.getInventoryCode())).findFirst().get();

        //增加可售库存, 那么需要减少虚拟库存，同时验证库存是否充足
        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_PLUS_AND_UNREAL_SUBTRACT) {
            if (i.getSaleStockNum() >= Math.abs(info.getInventoryQuantity())) {
                i.setSaleStockNum(CalcStockDataService.getOperatorResult(i.getSaleStockNum(), info.getInventoryQuantity(),
                        CommonConstant.STOCK_ACTION_RESPONSE.SUBTRACT));
            } else {
                //虚拟库存不足时，直接记为0
                i.setSaleStockNum(CommonConstant.ZERO);
            }
        }
        //减少可售库存，那么需要增加虚拟库存
        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_SUBTRACT_AND_UNREAL_PLUS) {
            i.setSaleStockNum(CalcStockDataService.getOperatorResult(i.getSaleStockNum(), info.getInventoryQuantity(),
                    CommonConstant.STOCK_ACTION_RESPONSE.PLUS));
        }
    }

    private void calcAndSetNewRealStockNum(DefinedSystem definedSystem, StockChangeInnerEventEnum changeInnerEventEnum,
                                           StockInventoryChangeSettings changeSettings,
                                           List<StockInventoryUnreal> inventoryUnrealList,
                                           StockInventoryReal inventoryReal, InventoryInfoVO info) {
        //增加可售库存
        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.PLUS ||
                changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_PLUS_AND_UNREAL_SUBTRACT) {
            inventoryReal.setSaleStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getSaleStockNum(), info.getInventoryQuantity(),
                    changeSettings.getSaleStockAction()));
        }
        //减少可售库存，判断库存是否充足
        if (changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.SUBTRACT
                || changeSettings.getSaleStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_SUBTRACT_AND_UNREAL_PLUS) {

            if (inventoryReal.getSaleStockNum() >= Math.abs(info.getInventoryQuantity())) {

                inventoryReal.setSaleStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getSaleStockNum(), info.getInventoryQuantity(),
                        changeSettings.getSaleStockAction()));
            } else {
                //库存不足异常
                log.error("可售库存不足: {}",
                        String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                                "actionType[", String.valueOf(changeInnerEventEnum.getName()), "]", "inventoryInfo[", JSONObject.toJSONString(info), "]"));
                String msg = String.format("%s;事件:%s, 货品编码: %s, 占用数量: %s, 库存中心可售数量: %s",
                        SURPLUS_SALES_STOCK_EXCEPTION_900202.getMsg(),
                        changeInnerEventEnum.getCode(),
                        info.getInventoryCode(),
                        info.getInventoryQuantity(),
                        inventoryReal.getSaleStockNum());
                throw new StockRuntimeException(SURPLUS_SALES_STOCK_EXCEPTION_900202.getCode(), msg);
            }
        }
        //增加占用库存(如销售下单)
        if (changeSettings.getUsedStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.PLUS ||
                changeSettings.getUsedStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_PLUS_AND_UNREAL_SUBTRACT) {
            Integer totalRealStockNum = stockWarehouseRelationService.getTotalRealStockNumByInventoryCode(info.getInventoryCode());
            //如果是采购中心占用库存，那么不校验可售数量，直接校验实物数
            if (definedSystem.equals(DefinedSystem.PURCHASE_CENTER)) {

                //这里使用"实物数+在途数 > 0"作为判断条件
                if((totalRealStockNum + inventoryReal.getFreeStockNum() - Math.abs(inventoryReal.getUsedStockNum()) - Math.abs(info.getInventoryQuantity()) ) >= 0){
                    inventoryReal.setUsedStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getUsedStockNum(), info.getInventoryQuantity(),
                            changeSettings.getUsedStockAction()));
                    return;
                }else {
                    //实物库存不足
                    String msg = printLogs(definedSystem, changeInnerEventEnum, inventoryReal, info, totalRealStockNum);
                    throw new StockRuntimeException(ADD_OCCUPY_STOCK_EXCEPTION_900201.getCode(), msg);
                }

            }

            if ((inventoryReal.getSaleStockNum() - inventoryReal.getUsedStockNum()) >= Math.abs(info.getInventoryQuantity())) {
                inventoryReal.setUsedStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getUsedStockNum(), info.getInventoryQuantity(),
                        changeSettings.getUsedStockAction()));
            } else {
                //判断是否虚拟库存，是虚拟库存就再验证虚拟库存数量够不够
                StockInventoryUnreal inventoryUnreal = inventoryUnrealList.stream().filter(f -> f.getInventoryCode().equals(inventoryReal.getInventoryCode())).findFirst().orElse(null);

                if (inventoryUnreal != null && (
                                //虚拟库存+实物库存充足
                                (inventoryUnreal.getSaleStockNum() + inventoryReal.getSaleStockNum() - inventoryReal.getUsedStockNum()) >= Math.abs(info.getInventoryQuantity()))
                ) {
                    inventoryReal.setUsedStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getUsedStockNum(), info.getInventoryQuantity(),
                            changeSettings.getUsedStockAction()));
                } else {
                    //库存不足异常
                    String msg = printLogs(definedSystem, changeInnerEventEnum, inventoryReal, info, totalRealStockNum);
                    throw new StockRuntimeException(ADD_OCCUPY_STOCK_EXCEPTION_900201.getCode(), msg);
                }
            }

        }
        //减少占用库存，判断库存是否充足(如取消订单、调拨申请)
        if (changeSettings.getUsedStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.SUBTRACT ||
                changeSettings.getUsedStockAction() == CommonConstant.STOCK_ACTION_RESPONSE.REAL_SUBTRACT_AND_UNREAL_PLUS) {
            if (inventoryReal.getUsedStockNum() >= Math.abs(info.getInventoryQuantity())) {
                inventoryReal.setUsedStockNum(CalcStockDataService.getOperatorResult(inventoryReal.getUsedStockNum(), info.getInventoryQuantity(),
                        changeSettings.getUsedStockAction()));
            } else {
                //库存不足异常
                log.error("释放占用库存不足: {}",
                        String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                                "actionType[", String.valueOf(changeInnerEventEnum.getName()), "],", "inventoryInfo[", JSONObject.toJSONString(info), "]"));

                String msg = String.format("%s;事件:%s, 货品编码: %s, 占用数量: %s, 库存中心实物类可售数量: %s",
                        RELEASE_OCCUPY_STOCK_EXCEPTION_900203.getMsg(),
                        changeInnerEventEnum.getCode(),
                        info.getInventoryCode(),
                        info.getInventoryQuantity(),
                        inventoryReal.getSaleStockNum());
                throw new StockRuntimeException(RELEASE_OCCUPY_STOCK_EXCEPTION_900203.getCode(), msg);
            }
        }
    }

    private String printLogs(DefinedSystem definedSystem, StockChangeInnerEventEnum changeInnerEventEnum, StockInventoryReal inventoryReal, InventoryInfoVO info, Integer totalRealStockNum) {
        if(totalRealStockNum == null){
            totalRealStockNum = 0;
        }
        log.error("增加占用库存不足: {}",
                String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                        "actionType[", String.valueOf(changeInnerEventEnum.getName()), "],", "inventoryInfo[", JSONObject.toJSONString(info), "]"));

        return String.format("%s;事件:%s, 货品编码: %s, 占用数量: %s, 可售数量: %s, 实物与在途数: %s",
                ADD_OCCUPY_STOCK_EXCEPTION_900201.getMsg(),
                changeInnerEventEnum.getCode(),
                info.getInventoryCode(),
                info.getInventoryQuantity(),
                inventoryReal.getSaleStockNum() - inventoryReal.getUsedStockNum(),
                totalRealStockNum - inventoryReal.getFreeStockNum());
    }

    private void checkRealInventoryDataIsCorrect(DefinedSystem definedSystem, StockChangeInnerEventEnum changeInnerEventEnum,
                                                 List<InventoryInfoVO> inventoryInfoList, List<StockInventoryReal> inventoryRealList) {
        if (CollectionUtils.isEmpty(inventoryRealList)) {
            //异常
            log.error("未找到货品实物类库存: {}",
                    String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                            "actionType[", String.valueOf(changeInnerEventEnum.getName()), "]", "inventoryInfoList[", JSONObject.toJSONString(inventoryInfoList), "]"));
            throw new StockRuntimeException(INVENTORY_EMPTY_EXCEPTION_900211);
        }

        for (InventoryInfoVO inventoryInfo : inventoryInfoList) {
            StockInventoryReal info = inventoryRealList.stream().filter(f -> f.getInventoryCode().equals(inventoryInfo.getInventoryCode())).findFirst().orElse(null);

            if (info == null) {
                //异常
                log.error("未找到货品实物类库存: {}",
                        String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                                "actionType[", String.valueOf(changeInnerEventEnum.getName()), "]", "inventoryInfo[", JSONObject.toJSONString(inventoryInfo), "]"));
                String msg = String.format("===>> 事件[%s]，货品编码[%s] ===>> 未找到货品编码对应的实物类库库存", changeInnerEventEnum.getCode(),
                        inventoryInfo.getInventoryCode());
                throw new StockRuntimeException(INVENTORY_EMPTY_EXCEPTION_900211.getCode(), msg);
            }
        }
    }

    private void checkChangeSettingsIsCorrect(DefinedSystem definedSystem, StockChangeInnerEventEnum changeInnerEventEnum, StockInventoryChangeSettings changeSettings) {
        //校验变动事件对应的配置数据是否正确
        if (changeSettings == null) {

            log.error("未找到事件所对应的settings配置: {}",
                    String.join("", "recordChannel[", String.valueOf(definedSystem), "], ",
                            "actionType[", String.valueOf(changeInnerEventEnum.getName()), "]")
            );
            throw new StockRuntimeException(SETTINGS_NOT_FOUND_900210);
        }
    }

    public void saveSalesLevelStockChangeLog(String docNo, int recordChannel, int actionType,
                                              List<InventoryInfoVO> inventoryInfoList,
                                              List<StockInventoryReal> inventoryRealList,
                                              List<StockInventoryUnreal> inventoryUnrealList) {
        //组装实物类日志信息
        List<SalesLevelInventoryChangeVO> realChangeVO = inventoryRealList.stream().map(i -> {
            SalesLevelInventoryChangeVO inventoryChangeVO = new SalesLevelInventoryChangeVO();
            BeanUtils.copyProperties(i, inventoryChangeVO);
            inventoryChangeVO.setStatus(CommonConstant.SUCCESS_OR_FAIL.SUCCESS)
                    .setRecordChannel(recordChannel)
                    .setActionType(actionType)
                    .setDirectSn(docNo)
                    .setUpdatedNum(inventoryInfoList.stream().filter(info -> info.getInventoryCode().equals(i.getInventoryCode())).findFirst().get().getInventoryQuantity())
                    .setOptRemark(StockChangeInnerEventEnum.getName(actionType))
                    .setGoodsType(CommonConstant.GOODS_TYPE.REAL)
                    //时间由数据库添加
                    .setCreateTime(null);

            return inventoryChangeVO;
        }).collect(Collectors.toList());

        //组装虚拟类日志信息
        List<SalesLevelInventoryChangeVO> unrealChangeVO = inventoryUnrealList.stream().map(i -> {
            SalesLevelInventoryChangeVO inventoryChangeVO = new SalesLevelInventoryChangeVO();
            BeanUtils.copyProperties(i, inventoryChangeVO);
            inventoryChangeVO.setStatus(CommonConstant.SUCCESS_OR_FAIL.SUCCESS)
                    .setRecordChannel(recordChannel)
                    .setActionType(actionType)
                    .setDirectSn(docNo)
                    //虚拟类实际变动数量与实物类相反，所以添加负号
                    .setUpdatedNum(-inventoryInfoList.stream().filter(info -> info.getInventoryCode().equals(i.getInventoryCode())).findFirst().get().getInventoryQuantity())
                    .setOptRemark(StockChangeInnerEventEnum.getName(actionType))
                    .setGoodsType(CommonConstant.GOODS_TYPE.UNREAL)
                    //时间由数据库添加
                    .setCreateTime(null);

            return inventoryChangeVO;
        }).collect(Collectors.toList());

        //保存日志
        stockInventoryChangeService.saveSalesLevelInventoryChangeLog(realChangeVO, unrealChangeVO);


    }
}
